﻿using vroom.db.model;
using vroom.wcf.service.cache;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity.Core.Objects;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using log4net;
using System.Reflection;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization.Formatters.Binary;
using vroom.wcf.dal;
using vroom.wcf.common;
using System.Threading;
using vroom.wcf.service.config;
using vroom.wcf.service.common;
using vroom.wcf.common.service;

namespace vroom.wcf.service
{
	[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
	public class PosService : IPosService
	{
		private CacheWrapper _cacheWrapper = CacheWrapper.Instance;
		private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.Name);

		bool waitOnObjectsToPopulate = true;
		int  maxWaitForObjectToPopulateDurationInSeconds = 80;
		int  waitOnObjectCacheDuration = 20 * 1000;

      
		public Position[] GetPositions(string userName, DateTime effectiveDate)
		{
			Position[] results = null;
			string cachekey = Consts.Position + Consts.HYPHEN + effectiveDate.ToIso();

			waitOnObjectsToPopulate = _cacheWrapper.WaitOnObjectsToPopulate;
			maxWaitForObjectToPopulateDurationInSeconds = _cacheWrapper.MaxWaitForObjectToPopulateDurationInSeconds;
			waitOnObjectCacheDuration = _cacheWrapper.WaitOnObjectCacheDurationInSeconds * 1000;

			if (_cacheWrapper.Contains(cachekey))
			{

				if (_cacheWrapper.TryGet(cachekey).Object != null)
				{
					ICachedObject cachedRes = _cacheWrapper.TryGet(cachekey);
					_log.Info("Using Cache, Key:" + cachekey + ", Created:" + cachedRes.UtcDateTimeInserted);
					results = (Position[])cachedRes.Object;
				}
				else
				{
					//Set Start & End times
					DateTime waitTick = DateTime.UtcNow;
					DateTime endTick  = DateTime.UtcNow + TimeSpan.FromSeconds(maxWaitForObjectToPopulateDurationInSeconds);

					while (waitTick <= endTick)
					{
						if (_cacheWrapper.TryGet(cachekey).Object == null)
						{
							_log.Info("Waiting Cache to be updated, Key:" + cachekey);
							waitTick = DateTime.UtcNow;
							Thread.Sleep(waitOnObjectCacheDuration);
						}
						else
						{
							ICachedObject cachedRes = _cacheWrapper.TryGet(cachekey);
							_log.Info("Using Cache, Key:" + cachekey + ", Created:" + cachedRes.UtcDateTimeInserted);
							results = (Position[])cachedRes.Object;
							break;
						}
					}

					if (results == null)
						_log.Info("Cache Miss (Wait timeout), Key:" + cachekey);
				}

				return results;
			}
			else
			{
			
				try
				{
					_log.Info("No Cache found, fetching from db");

					if (waitOnObjectsToPopulate) //Only if set to true set placeholders
						_cacheWrapper.Set(new CachedObject(cachekey, effectiveDate, null), cachekey);					

					Position[] res = new PosDataAccess().GetPositions(effectiveDate);
					results = res.ToArray<Position>();

					_log.Info("Using db data");
					_cacheWrapper.Set(new CachedObject(cachekey, effectiveDate, results), cachekey);
				}
				catch (Exception e)
				{
					_cacheWrapper.Remove(cachekey);
					_log.Info("Error in updating Cache Key:" + cachekey + ", removing item from cache", e);
				}

				return results;

			}

		}



		public Bucket[] GetBucketedPositions(DateTime effectiveDate)
		{
			Bucket[] results = null;
			string cachekey = Consts.Bucket + Consts.HYPHEN + effectiveDate.ToIso();

			waitOnObjectsToPopulate = _cacheWrapper.WaitOnObjectsToPopulate;
			maxWaitForObjectToPopulateDurationInSeconds = _cacheWrapper.MaxWaitForObjectToPopulateDurationInSeconds;
			waitOnObjectCacheDuration = _cacheWrapper.WaitOnObjectCacheDurationInSeconds * 1000;

			if (_cacheWrapper.Contains(cachekey))
			{

				if (_cacheWrapper.TryGet(cachekey).Object != null)
				{
					ICachedObject cachedRes = _cacheWrapper.TryGet(cachekey);
					_log.Info("Using Cache, Key:" + cachekey + ", Created:" + cachedRes.UtcDateTimeInserted);
					results = (Bucket[])cachedRes.Object;
				}
				else
				{
					//Set Start & End times
					DateTime waitTick = DateTime.UtcNow;
					DateTime endTick = DateTime.UtcNow + TimeSpan.FromSeconds(maxWaitForObjectToPopulateDurationInSeconds);

					while (waitTick <= endTick)
					{
						if (_cacheWrapper.TryGet(cachekey).Object == null)
						{
							_log.Info("Waiting Cache to be updated, Key:" + cachekey);
							waitTick = DateTime.UtcNow;
							Thread.Sleep(waitOnObjectCacheDuration);
						}
						else
						{
							ICachedObject cachedRes = _cacheWrapper.TryGet(cachekey);
							_log.Info("Using Cache, Key:" + cachekey + ", Created:" + cachedRes.UtcDateTimeInserted);
							results = (Bucket[])cachedRes.Object;
							break;
						}
					}

					if (results == null)
						_log.Info("Cache Miss (Wait timeout), Key:" + cachekey);
				}

				return results;
			}
			else
			{

				try
				{
					_log.Info("No Cache found, fetching from db");
					if (waitOnObjectsToPopulate) //Only if set to true set placeholders
						_cacheWrapper.Set(new CachedObject(cachekey, effectiveDate, null), cachekey);

                    Bucket[] res = new PosDataAccess().GetBucketedPositions(effectiveDate);
					results = res.ToArray<Bucket>();

					_log.Info("Using db data");
					_cacheWrapper.Set(new CachedObject(cachekey, effectiveDate, results), cachekey);
				}
				catch (Exception e)
				{
					_cacheWrapper.Remove(cachekey);
					_log.Info("Error in updating Cache Key:" + cachekey + ", removing item from cache", e);
				}

				return results;

			}
		}
	}
}
